package cn.com.thinker.core.dao.jpa.specs;

import com.google.common.collect.Iterables;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.lang.reflect.Field;

/**
 * Created by crazyHC on 2017/5/31.
 */
public class LikeSpecs {

    @SuppressWarnings("unchecked")
	public static <T> Specification<T> byAuto(final EntityManager entityManager, final T entity) {

        final Class<T> type = (Class<T>) entity.getClass();

        return new Specification<T>() {

            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>();

                EntityType<T> entityType = entityManager.getMetamodel().entity(type);

                for (Attribute<T, ?> attr : entityType.getDeclaredAttributes()) {
                    Object attrValue = getValue(entity, attr);
                    if (attrValue != null) {
                        if (attr.getJavaType() == String.class) {
                            if (!StringUtils.isEmpty(attrValue)) {
                                predicates.add(cb.like(root.get(attribute(entityType, attr.getName(), String.class)),
                                        pattern((String) attrValue)));
                            }
                        } else {
                            predicates.add(cb.equal(root.get(attribute(entityType, attr.getName(), attrValue.getClass())),
                                    attrValue));
                        }
                    }

                }
                return predicates.isEmpty() ? cb.conjunction() : cb.and(Iterables.toArray(predicates, Predicate.class));
            }

            /**
             * 获取属性值
             */
            private Object getValue(T entity, Attribute<T, ?> attr) {
                return ReflectionUtils.getField((Field) attr.getJavaMember(), entity);
            }

            /**
             * 获取单个属性
             */
            private <E> SingularAttribute<T, E> attribute(EntityType<T> entityType, String fieldName,
                                                             Class<E> fieldClass) {
                return entityType.getDeclaredSingularAttribute(fieldName, fieldClass);
            }

        };

    }

    /**
     * 模糊查询
     */
    static private String pattern(String str) {
        return "%" + str + "%";
    }
}
